/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package airacrobats.gear;

import airacrobats.control.Controller;
import airacrobats.control.ReplayController;
import airacrobats.game.Camera;
import airacrobats.game.Squadron;
import airacrobats.game.Stage;
import airacrobats.game.object.Airplane;
import airacrobats.game.object.GameObject;
import airacrobats.game.object.GhostAirplane;
import airacrobats.game.object.Sky;
import airacrobats.game.Smoker;
import airacrobats.game.object.Terrain;
import airacrobats.gui.component.ALabel;
import airacrobats.gui.component.ATimerLabel;
import airacrobats.gui.container.GameContainer;
import airacrobats.io.data.FlapsData;
import airacrobats.io.data.GhostData;
import airacrobats.io.data.JWavefrontModel;
import airacrobats.io.data.RouteData;
import airacrobats.sound.RadioSound;
import airacrobats.sound.Sound;
import airacrobats.sound.Sound3D;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLProfile;
import javax.sound.sampled.LineUnavailableException;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

/**
 * This is the gear where the replay runs.
 */
public class ReplayGear extends Gear implements ActionListener
{
    /**
     * Frames per second. It is higher than the game gear to optimize the replay.
     */
    private final static int FPS = 30;
    /**
     * Size of the game screen.
     */
    private final static Dimension REPLAY_PANEL_SIZE = new Dimension(800, 600);
    /**
     * The number of bits passed to the capabilities.
     */
    private final static int CAP_BITS = 16;

    /**
     * Game timer.
     */
    private Timer gameTimer;
    /**
     * Game screen panel.
     */
    private GameContainer gameContainer;
    /**
     * The message label.
     */
    private JLabel messageLabel;
    /**
     * The misc panel.
     */
    private JPanel miscPanel;
    /**
     * Squadron.
     */
    private Squadron selectedSquadron;
    /**
     * Stage.
     */
    private Stage stage;
    /**
     * The game camera.
     */
    private Camera camera;
    /**
     * The gear to change when the stage time is over.
     */
    private Gear gear;
    /**
     * The ghosts of the airplanes.
     */
    private List<List<GhostData>> airplanesGhosts;
    /**
     * The airplanes.
     */
    private List<Airplane> airplanes;
    /**
     * The flashing replay label.
     */
    private ATimerLabel replayLabel;
    /**
     * The replay music.
     */
    private Sound replaySound;
    /**
     * Smokers.
     */
    private List<Smoker> smokers;
    /**
     * Replay controller.
     */
    private ReplayController controller;

    /**
     * @param gearListener The gear listener.
     * @param gear The gear to change when the stage is over.
     * @param selectedSquadron The selected squadron.
     * @param stage The stage.
     * @param airplanesGhosts The airplanes ghosts list.
     */
    public ReplayGear(GearListener gearListener, Gear gear, Squadron selectedSquadron, Stage stage,
            List<List<GhostData>> airplanesGhosts)
    {
        super(gearListener);
        this.gear = gear;
        this.selectedSquadron = selectedSquadron;
        this.stage = stage;
        this.airplanesGhosts = airplanesGhosts;
    }

    /**
     */
    @Override
    public boolean closing()
    {
        changeGear();
        return false;
    }

    /**
     */
    @Override
    public void load()
    {
        airplanes = new ArrayList<Airplane>();

        JWavefrontModel model = wavefrontLoader.get(selectedSquadron.getType().getMapKey()
                + "/Airplane.obj");
        JWavefrontModel shadowModel = wavefrontLoader.get(selectedSquadron.getType().getMapKey()
                + "/AirplaneShadow.obj");
        JWavefrontModel smokeModel = wavefrontLoader.get(selectedSquadron.getType().getMapKey()
                + "/Smoke.obj");

        List<RouteData> routes = stage.getStageData().getRoutes();

        smokers = new ArrayList<Smoker>();

        JWavefrontModel propellerModel = wavefrontLoader.get(selectedSquadron.getType().getMapKey()
                    + "/Propeller.obj");

        FlapsData[] flapsDataList = flapsLoader.getObjects().toArray(new FlapsData[0]);
        JWavefrontModel[] flapsModels = new JWavefrontModel[flapsDataList.length];
        int i = 0;
        for (FlapsData flapsData : flapsDataList)
        {
            flapsModels[i++] = wavefrontLoader.get(selectedSquadron.getType().getMapKey()
                    + "/" + flapsData.getModelName() + ".obj");
        }
        
        Iterator<List<GhostData>> airplanesGhostsIt = airplanesGhosts.iterator();
        
        for (RouteData routeData : routes)
        {         
            Sound3D[] engineSounds = new Sound3D[0];

            GhostAirplane airplane = new GhostAirplane(model, shadowModel, flapsModels, propellerModel,
                    flapsDataList, new Point3f(routeData.getStartPosition()),
                    selectedSquadron.getSquadronData().getAirplaneData(),
                    new Vector3f(routeData.getStartSpeed()), routeData.isInverted(), engineSounds,
                    airplanesGhostsIt.next());

            if (airplanes.isEmpty())
            {
                camera = new Camera(airplane, null);
                camera.changeCamera(true, true);
                controller = new ReplayController(airplane, camera);
            }

            smokers.add(new Smoker(smokeModel, airplane));

            airplanes.add(airplane);
        }

        Terrain terrain = new Terrain(wavefrontLoader.get(selectedSquadron.getType().getMapKey()
                + "/Terrain.obj"), new ArrayList<GameObject>(airplanes),
                new Point3f(0.0f, Integer.MAX_VALUE, 0.0f), Camera.SIGHT);

        Sky sky = new Sky(wavefrontLoader.get(selectedSquadron.getType().getMapKey()
                + "/Sky.obj"), Camera.SIGHT);

        List<GameObject> objects = new ArrayList<GameObject>();
        objects.add(terrain);
        objects.add(sky);
        objects.addAll(1, airplanes);

        GLCapabilities capabilities = new GLCapabilities(GLProfile.getDefault());
        capabilities.setAccumBlueBits(CAP_BITS);
        capabilities.setAccumGreenBits(CAP_BITS);
        capabilities.setAccumRedBits(CAP_BITS);
        capabilities.setDoubleBuffered(true);
        capabilities.setHardwareAccelerated(true);

        List<RadioSound> radioSounds = new ArrayList<RadioSound>();

        List<Controller> controllers = new ArrayList<Controller>();
        // Note: Even though the controller is passed to the game container, its update will do nothing,
        // since the replay controller only controls the camera.
        controllers.add(controller);
        
        gameContainer = new GameContainer(capabilities, REPLAY_PANEL_SIZE, objects, camera, 
                controllers, radioSounds, null, stage, smokers);

        messageLabel = new ALabel("Press any key to start. "
                + "Press ESC at any time to return to the main menu.", Color.WHITE, Color.BLACK);

        miscPanel = new JPanel();
        miscPanel.setOpaque(false);
        miscPanel.setLayout(new GridBagLayout());

        replayLabel = new ATimerLabel("Replay", Color.YELLOW, Color.BLACK, 800);

        replaySound = new Sound(soundLoader.get(selectedSquadron.getType().getMapKey()
                + "/Replay.ogg"));

        GridBagConstraints c = new GridBagConstraints();

        c.gridx = 0;
        c.gridy = 0;
        miscPanel.add(replayLabel, c);

        c.gridy++;
        camera.createInfoPanel(miscPanel, c);

        c.gridy++;
        stage.createInfoPanel(miscPanel, c);

        addKeyListener(new KeyAdapter()
        {
            @Override
            public void keyPressed(KeyEvent e)
            {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
                {
                    changeGear();
                }
                else if (e.getKeyCode() == KeyEvent.VK_R)
                {
                    if (stage.getRemainingTime() <= 0)
                        replayStage();
                }
                else if (!gameContainer.isStarted())
                {
                    try
                    {
                        gameContainer.setStarted();
                        messageLabel.setText("");
                        replaySound.play();
                    }
                    catch (LineUnavailableException ex)
                    {
                        Thread.getDefaultUncaughtExceptionHandler().uncaughtException(null, ex);
                    }
                }
            }
        });

        addKeyListener(controller);
        gameTimer = new Timer(1000 / FPS, ReplayGear.this);
        gameTimer.start();

        c = new GridBagConstraints();

        c.gridx = 0;
        c.gridy = 0;
        add(gameContainer, c);

        c.gridx = 0;
        c.gridy = 0;
        c.anchor = GridBagConstraints.NORTH;
        gameContainer.add(miscPanel, c);

        c.weighty = 1.0f;
        c.gridy++;
        gameContainer.add(messageLabel, c);

        stage.start(false);
    }

    /**
     */
    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource() == gameTimer)
        {
            if (stage.getRemainingTime() <= 0)
            {
                gameTimer.stop();
                replayLabel.stop();
                replaySound.stop();
                gameContainer.stopSounds();
                messageLabel.setText("Press ESC key to return or R to replay again.");
                removeKeyListener(controller);
            }
            // This will trigger the main loop method of the game later.
            gameContainer.repaint();
        }
    }

    /**
     * Starts the replay.
     */
    private void replayStage()
    {
        if (stage.getRemainingTime() > 0)
            return;

        // It will only replay again if the time is over. At this point, everything were already stopped/closed.
        Gear replayGear = new ReplayGear(gearListener, gear, selectedSquadron, stage, airplanesGhosts);
        replayGear.load();
        gearListener.gearChanging(replayGear);
    }
    
    /**
     * Changes this context to the new gear context.
     */
    private void changeGear()
    {
        gameTimer.stop();
        replayLabel.stop();
        replaySound.stop();
        gameContainer.stopSounds();
        gear.load();
        gearListener.gearChanging(gear);
    }
}
